package com.example.demo001.service.impl;

import com.example.demo001.common.result.Result;
import com.example.demo001.constant.JwtClaimsConstant;
import com.example.demo001.constant.MessageConstant;
import com.example.demo001.constant.StatusCode;
import com.example.demo001.context.BaseContext;
import com.example.demo001.exception.BaseException;
import com.example.demo001.mapper.LoginLogMapper;
import com.example.demo001.mapper.UserMapper;
import com.example.demo001.pojo.dto.UserDto;
import com.example.demo001.pojo.dto.UserLoginDto;
import com.example.demo001.pojo.entity.LoginLog;
import com.example.demo001.pojo.entity.User;
import com.example.demo001.pojo.vo.LoginStatistVo;
import com.example.demo001.pojo.vo.UserVo;
import com.example.demo001.service.UserService;
import com.example.demo001.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.routines.EmailValidator;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;
    private final LoginLogMapper loginLogMapper;
    private final RedisTemplate<String, String> redisTemplate;

    // 使用构造函数注入
    @Autowired
    public UserServiceImpl(UserMapper userMapper, LoginLogMapper loginLogMapper, RedisTemplate<String, String> redisTemplate) {
        this.userMapper = userMapper;
        this.loginLogMapper = loginLogMapper;
        this.redisTemplate = redisTemplate;
    }
    /**
     * 注册
     */
    @Override
    public void register(UserDto userDto) {

        //去除用户名前后空格
        userDto.setUsername(userDto.getUsername().trim());
        //用户名格式校验
        if (UsernameValidatorUtil.isValidUsername(userDto.getUsername())) {
            throw new BaseException(StatusCode.USERNAME_FORMAT_ERROR, MessageConstant.USERNAME_FORMAT_ERROR);
        }
        //邮箱格式校验
        boolean isValid = EmailValidator.getInstance().isValid(userDto.getEmail());
        if (!isValid) {
            throw new BaseException(StatusCode.EMAIL_FORMAT_ERROR, MessageConstant.EMAIL_FORMAT_ERROR);
        }
        //密码校验
        boolean validPassword = PasswordValidatorUtil.isValidPassword(userDto.getPassword());
        if (!validPassword) {
            throw new BaseException(StatusCode.PASSWORD_FORMAT_ERROR, MessageConstant.PASSWORD_FORMAT_ERROR);
        }
        //验证码校验
        // 判断验证码是否过期
        String code = getCode(userDto.getEmail());
        if (StringUtils.isBlank(code)) {
            throw new BaseException(StatusCode.EMAIL_CODE_EXPIRED, MessageConstant.EMAIL_CODE_EXPIRED);
        }
        // 判断验证码是否正确
        if (!userDto.getCode().equals(code)) {
            throw new BaseException(StatusCode.CODE_ERROR, MessageConstant.CODE_ERROR);
        }
        //判断用户名是否已经存在
        Integer id = userMapper.findName(userDto.getUsername());
        if (id != null) {
            throw new BaseException(StatusCode.USERNAME_ALREADY_EXISTS, MessageConstant.USERNAME_ALREADY_EXISTS);
        }
        //判断邮箱是否已经存在
        Integer idByEmail =userMapper.findEmail(userDto.getEmail());
        if (idByEmail != null) {
            throw new BaseException(StatusCode.EMAIL_ALREADY_EXISTS, MessageConstant.EMAIL_ALREADY_EXISTS);
        }
        String hashPw = BCrypt.hashpw(userDto.getPassword(), BCrypt.gensalt());
        userDto.setPassword(hashPw);

        User user = new User();
        BeanUtils.copyProperties(userDto, user);
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        user.setStatus(MessageConstant.USER_STATUS_NORMAL);
        userMapper.register(user);

        //删除redis中的验证码
        redisTemplate.delete(userDto.getEmail());
    }

    private String getCode(String userDto) {
        return redisTemplate.opsForValue().get(userDto);
    }

    @Override
    public UserVo login(UserLoginDto userLoginDto, HttpServletRequest request) {

        //去除用户名前后空格
        userLoginDto.setUsername(userLoginDto.getUsername().trim());
        //用户名格式校验
        if (UsernameValidatorUtil.isValidUsername(userLoginDto.getUsername())) {
            throw new BaseException(StatusCode.USERNAME_FORMAT_ERROR, MessageConstant.USERNAME_FORMAT_ERROR);
        }
        //密码校验
        boolean validPassword = PasswordValidatorUtil.isValidPassword(userLoginDto.getPassword());
        if (!validPassword) {
            throw new BaseException(StatusCode.PASSWORD_FORMAT_ERROR, MessageConstant.PASSWORD_FORMAT_ERROR);
        }
        //判断用户名是否存在
        User user = userMapper.selectByUsername(userLoginDto.getUsername());
        if (Objects.isNull(user)) {
            throw new BaseException(StatusCode.USERNAME_NOT_EXISTS, MessageConstant.USERNAME_NOT_EXISTS);
        }
        if (!BCrypt.checkpw(userLoginDto.getPassword(), user.getPassword())) {
            Long increment = redisTemplate.opsForValue().increment(user.getUsername(), MessageConstant.DELTA);
            redisTemplate.expire(user.getUsername(), MessageConstant.PASSWORD_ERROR_KEY_EXPIRE_TIME, TimeUnit.SECONDS);
            log.info("用户名：{} 密码错误次数：{}", user.getUsername(), increment);
            // 检查返回值是否为 null
            if (increment == null) {
                increment = 0L;
                log.warn("Redis increment operation returned null for key: {}", user.getUsername());
            }
            if (increment > 10) {
                //锁定账户
                userMapper.updateStatus(user.getId(), MessageConstant.USER_STATUS_LOCKED);
                throw new BaseException(StatusCode.USER_STATUS_LOCKED_ERROR, MessageConstant.USER_STATUS_LOCKED_ERROR);
            }
            throw new BaseException(StatusCode.PASSWORD_ERROR, MessageConstant.PASSWORD_ERROR, increment);
        }
        if (user.getStatus().equals(MessageConstant.USER_STATUS_LOCKED)) {
            throw new BaseException(StatusCode.USER_STATUS_LOCKED_ERROR, MessageConstant.USER_STATUS_LOCKED_ERROR);
        }

        UserVo userVo = loginLogMapper.findUserVoByUsername(userLoginDto.getUsername());


        // 记录登录日志
        LoginLog loginLog = new LoginLog();
        loginLog.setUserId(user.getId());
        String clientIp = IpUtils.getClientIPv4Address(request);
        loginLog.setIp(clientIp);
        loginLog.setLoginTime(LocalDateTime.now());
        loginLogMapper.insert(loginLog);
        return userVo;
    }

    @Override
    public void logout(String username) {
        // 删除redis中该用户名下的所有token
        redisTemplate.delete(JwtClaimsConstant.TOKEN+username);
    }

    @Override
    public void updatePassword(String oldPassword, String newPassword) {

        if(StringUtils.isBlank(oldPassword)){
            throw new BaseException(StatusCode.PARAM_ERROR, MessageConstant.OLD_PASSWORD_NOT_NULL);
        }
        if(StringUtils.isBlank(newPassword)){
            throw new BaseException(StatusCode.PARAM_ERROR, MessageConstant.NEW_PASSWORD_NOT_NULL);
        }
        if (!PasswordValidatorUtil.isValidPassword(oldPassword)) {
            throw new BaseException(StatusCode.OLD_PASSWORD_FORMAT_ERROR, MessageConstant.OLD_PASSWORD_FORMAT_ERROR);
        }

        if (!PasswordValidatorUtil.isValidPassword(newPassword)) {
            throw new BaseException(StatusCode.NEW_PASSWORD_FORMAT_ERROR, MessageConstant.NEW_PASSWORD_FORMAT_ERROR);
        }
        String username = BaseContext.getCurrent();
        User user = userMapper.selectByUsername(username);
        boolean checkPW = BCrypt.checkpw(oldPassword, user.getPassword());
        if (!checkPW) {
            throw new BaseException(StatusCode.OLD_PASSWORD_ERROR, MessageConstant.OLD_PASSWORD_ERROR);
        }
        String hashPW = BCrypt.hashpw(newPassword, BCrypt.gensalt());

        userMapper.updatePassword(username, hashPW, LocalDateTime.now());

        // 删除redis中该用户名下的所有token
        redisTemplate.delete(JwtClaimsConstant.TOKEN+ username);

    }

    @Override
    public void updateEmail(String email,String code) {
        if (StringUtils.isBlank(email)) {
            throw new BaseException(StatusCode.PARAM_ERROR, MessageConstant.EMAIL_NOT_NULL);
        }


        //验证码校验
        // 判断验证码是否过期
        String redisCode = getCode(email);
        if (StringUtils.isBlank(redisCode)) {
            throw new BaseException(StatusCode.EMAIL_CODE_EXPIRED, MessageConstant.EMAIL_CODE_EXPIRED);
        }
        // 判断验证码是否正确
        if (!code.equals(redisCode)) {
            throw new BaseException(StatusCode.CODE_ERROR, MessageConstant.CODE_ERROR);
        }

        //邮箱格式校验
        log.info("email-->{} ", email);

        boolean isValid = EmailValidator.getInstance().isValid(email);
        if (!isValid) {
            throw new BaseException(StatusCode.EMAIL_FORMAT_ERROR, MessageConstant.EMAIL_FORMAT_ERROR);
        }

        Integer id = userMapper.selectByEmail(email);
        if (Objects.nonNull(id)) {
            throw new BaseException(StatusCode.EMAIL_ALREADY_EXISTS, MessageConstant.EMAIL_ALREADY_EXISTS);
        }

        userMapper.updateEmail(BaseContext.getCurrent(), email, LocalDateTime.now());
        //删除redis中的验证码
        redisTemplate.delete(email);
    }

    /**
     * 找回密码--> 重置密码 通过邮箱发送验证码的重置
     * 参数 username + email + code + newPassword
     */
    @Override
    public void resetPassword(UserDto userDto) {
        //去除用户名前后空格
        userDto.setUsername(userDto.getUsername().trim());
        // 新密码格式校验
        if (!PasswordValidatorUtil.isValidPassword(userDto.getPassword())) {
            throw new BaseException(StatusCode.NEW_PASSWORD_FORMAT_ERROR, MessageConstant.NEW_PASSWORD_FORMAT_ERROR);
        }
        // 邮箱格式校验
        if (!EmailValidator.getInstance().isValid(userDto.getEmail())) {
            throw new BaseException(StatusCode.EMAIL_FORMAT_ERROR, MessageConstant.EMAIL_FORMAT_ERROR);
        }
        // 用户名格式校验
        if (UsernameValidatorUtil.isValidUsername(userDto.getUsername())) {
            throw new BaseException(StatusCode.USERNAME_FORMAT_ERROR, MessageConstant.USERNAME_FORMAT_ERROR);
        }
        User user = userMapper.selectMailByUsername(userDto.getUsername());
        String redisCode = getCode(user.getEmail());
        //验证码校验
        // 判断验证码是否过期
        if (StringUtils.isBlank(redisCode)) {
            throw new BaseException(StatusCode.EMAIL_CODE_EXPIRED, MessageConstant.EMAIL_CODE_EXPIRED);
        }
        // 判断验证码是否正确
        if (!userDto.getCode().equals(redisCode)) {
            throw new BaseException(StatusCode.CODE_ERROR, MessageConstant.CODE_ERROR);
        }
        // 验证码校验成功后重设密码
        if(!user.getEmail().equals(userDto.getEmail())){
            throw new BaseException(StatusCode.EMAIL_NOT_SAME, MessageConstant.EMAIL_NOT_SAME);
        }

        String newPassword = BCrypt.hashpw(userDto.getPassword(), BCrypt.gensalt());
        userMapper.resetPassword(user.getUsername(), newPassword, LocalDateTime.now(),MessageConstant.USER_STATUS_NORMAL);
        log.info("新的密码为：{}", userDto.getPassword());


        // 删除redis中该用户名下的所有token
        redisTemplate.delete(JwtClaimsConstant.TOKEN+ userDto.getUsername());
        //删除redis中的验证码
        redisTemplate.delete(userDto.getEmail());
    }

    /**
     * 登录统计
     */
    @Override
    public Map<String , List<String>> loginStatist() {

        String username = BaseContext.getCurrent();
        ArrayList<LoginStatistVo> loginStatist = loginLogMapper.loginStatist(username);
        Map<String, List<String>> map = new HashMap<>();


        List<String> days = new ArrayList<>();
        List<String> counts = new ArrayList<>();

        for (LoginStatistVo loginStatisVo : loginStatist) {
            days.add(loginStatisVo.getDay());
            counts.add(loginStatisVo.getCount().toString());
        }
        map.put("days", days);
        map.put("counts", counts);
        return map;
    }

    @Override
    public Result<Object> sendCode(String email) {
        if(StringUtils.isBlank(email)){
            throw new BaseException(StatusCode.PARAM_ERROR, MessageConstant.EMAIL_NOT_NULL);
        }
        if(!EmailValidator.getInstance().isValid(email)){
            throw new BaseException(StatusCode.EMAIL_FORMAT_ERROR, MessageConstant.EMAIL_FORMAT_ERROR);
        }
        String code = CaptchaUtil.creatCode(MessageConstant.EMAIL_CODE_NUM);
        redisTemplate.opsForValue().set(email, code, MessageConstant.EMAIL_CODE_EXPIRE_TIME, TimeUnit.SECONDS);
        Set<String> set = new HashSet<>();
        set.add(email);
        boolean isMail = JavaMailUntil.sendEmail(set, "验证码", "您的验证码为:" + code + "\n1分钟有效请尽快使用。");
        if (!isMail) {
            return Result.error(MessageConstant.SEND_EMAIL_ERROR);
        }
        return Result.success(MessageConstant.SEND_EMAIL_SUCCESS, null);
    }


}
